﻿
#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using Microsoft.Surface.Presentation.Input;
using ScrumTable.Common;
using ScrumTable.UI.View.Adorners;
using ScrumTable.UI.View.Util;
using ScrumTable.UI.View.WinTouch.UserControls;

#endregion

namespace ScrumTable.UI.View.WinTouch.Util
{
    /// <summary>
    /// Removes the touch "handlers".
    /// Without this, ComboBox does not work (and all other non-touch-wpf controls)  Don't know why it works...but it works! 
    /// </summary>
    internal class WithoutTouchHandlerScatterItem : ScatterViewItem
    {
        protected override void OnTouchDown(TouchEventArgs e)
        {

        }
        protected override void OnPreviewTouchDown(TouchEventArgs e)
        {
            
        }

        protected override void OnTouchUp(TouchEventArgs e)
        {

        }
        protected override void OnPreviewTouchUp(TouchEventArgs e)
        {

        }
    }
    /// <summary>
    /// ScatteredViewOpener
    /// </summary>
    public class ScatteredViewOpener
    {


        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private readonly ScrumUserControl _childControl;
        private Point _pointOnScreen;
        private readonly bool _hasBorder;
        private readonly bool  _canScale;
        private readonly string _title;
        private readonly bool _isSingleItem;
        private readonly ScatterView _scatterView;
        private readonly AppScreen _appScreen;
        private readonly bool _centerOnMousePoint;
        private readonly Binding _borderBinding;
        private readonly string _bottomText;
        private readonly AdornerLayer[] _adornerLayer = { null };
        private readonly ResizingAdorner[] _adorner = { null };
        private SurfaceButton _button;
        private ScatterViewItem _item;
        private Grid _grid;
        //close with double click
        readonly ClickUtil _click = new ClickUtil(2);
        readonly ClickUtil _clickTouch = new ClickUtil(2);
        private const int BorderWidth = 30;

        private double _oldWidth;
        private double _oldHeight;
        private double _oldOrientation;
        private Point? _oldCenter;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------
        /// <summary>
        /// Returns the scatterviewitem
        /// </summary>
        public ScatterViewItem Item
        {
            get { return _item; }
        }

        #endregion

        #region Constructor
        //--------------------------------------------------------------------
        // Constructor
        //--------------------------------------------------------------------


        /// <summary>
        /// Adds an UserControl to the front toolwindow
        /// Adds a click event for removing this window from the toolwindow
        /// Adds a resize event
        /// </summary>
        /// <param name="childControl">The obj.</param>
        /// <param name="pointOnScreen">The center.</param>
        /// <param name="hasBorder">Adds an border to the item</param>
        /// <param name="canScale">if set to <c>true</c> then the item can be scaled in size.</param>
        /// <param name="title">titel text, placed on the middle of the border.</param>
        /// <param name="isSingleItem">if true only one type of this window is placed as ScatteredItem.</param>
        /// <param name="view">place the item on this scatterview</param>
        /// <param name="appScreen"></param>
        /// <param name="centerOnMousePoint">if true, the item will open under the given point, otherwise it will get center on it</param>
        /// <param name="borderBinding"></param>
        /// <param name="bottomText">shows this text on the border (bottom) only if bottom visible</param>
        public ScatteredViewOpener(ScrumUserControl childControl, Point pointOnScreen, bool hasBorder, bool canScale, string title, bool isSingleItem, ScatterView view, AppScreen appScreen, bool centerOnMousePoint = false, Binding borderBinding = null, string bottomText = null)
        {
            PreCondition.AssertNotNull(childControl, "childControl");

            _childControl = childControl;
            _pointOnScreen = pointOnScreen;
            _hasBorder = hasBorder;
            _canScale = canScale;
            _appScreen = appScreen;
            _centerOnMousePoint = centerOnMousePoint;
            _borderBinding = borderBinding;
            _bottomText = bottomText;
            _scatterView = view;
            _title = title;
            _isSingleItem = isSingleItem;

            SetObjSize();

            if (!IsObjAlreadyOpenAndNotAllowedToOpenAgain())
            {
                InitScatteredViewItem();
                InitEvents();
                InitBorder();
                _scatterView.Items.Add(_item);
                InitResizeUtil();
                SelectCurrentAddedItem();
            }
            else
            {
                childControl.Dispose();
            }
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------


        /// <summary>
        /// Sets the rotated coordinate from a control relativ to control placed on a scatterviewItem. 
        /// </summary>
        /// <param name="parentItem">the </param>
        /// <param name="controlOnScatterItem"></param>
        /// <param name="relativeTo"></param>
        /// <param name="positionOnTheControl">The positon on the control (Right corner or right top corner)</param>
        /// <param name="isMainWindowRotated"></param>
        public static void SetsRotatedPositionOnScatterItem(ScatterViewItem parentItem, Control controlOnScatterItem, FrameworkElement relativeTo, Point positionOnTheControl, bool isMainWindowRotated)
        {
            Point point = relativeTo.PointToScreen(positionOnTheControl);
            point = Application.Current.MainWindow.PointFromScreen(point);

            controlOnScatterItem.HorizontalAlignment = HorizontalAlignment.Left;
            controlOnScatterItem.VerticalAlignment = VerticalAlignment.Top;
            controlOnScatterItem.Margin = new Thickness(0, 0, 0, 0);
            controlOnScatterItem.HorizontalContentAlignment = HorizontalAlignment.Center;
            controlOnScatterItem.VerticalContentAlignment = VerticalAlignment.Center;


            TransformGroup group = new TransformGroup();
            var transform = new RotateTransform(parentItem == null ? 0 : parentItem.Orientation) { CenterX = point.X, CenterY = point.Y };

            if (isMainWindowRotated)
            {
                double y = Application.Current.MainWindow.Height - point.Y;
                double x = Application.Current.MainWindow.Width - point.X;
                transform.CenterX = 0;
                transform.CenterY = 0;
                group.Children.Add(transform);
                group.Children.Add(new TranslateTransform(x, y));
            }
            else
            {
                group.Children.Add(new TranslateTransform(point.X, point.Y));
                group.Children.Add(transform);
            }
            controlOnScatterItem.RenderTransform = group;
        }

        private void RemoveScatteredItemFromView()
        {
            _appScreen.RemoveScattertItem(_item);
            _childControl.Dispose();
            if (_adornerLayer[0] != null && _adorner[0] != null)
            {
                _adornerLayer[0].Remove(_adorner[0]);
            }
        }

        private void SetObjSize()
        {
            //!_canScale objects gets there size on the first resize event
            if (_canScale)
            { 
                //gets the width/height 
                _childControl.Measure(new Size(double.MaxValue, double.MaxValue));
                _childControl.Width = double.IsNaN(_childControl.Width)? _childControl.DesiredSize.Width: _childControl.Width;
                _childControl.Height = double.IsNaN(_childControl.Height)? _childControl.DesiredSize.Height: _childControl.Height;
            }
            _pointOnScreen = _scatterView.PointFromScreen(_pointOnScreen);
            if (_centerOnMousePoint && !double.IsNaN(_childControl.Width) && !double.IsNaN(_childControl.Height))
            {
                _pointOnScreen = new Point(_pointOnScreen.X + _childControl.Width / 2 + (_hasBorder ? BorderWidth / 4 : 0), _pointOnScreen.Y + _childControl.Height / 2 + (_hasBorder ? BorderWidth / 4 : 0));    
            }
            _pointOnScreen = GetScatteredItemPosition(_pointOnScreen, _childControl);
        }


        private Point GetScatteredItemPosition(Point center, FrameworkElement obj)
        {

            if (_appScreen.IsRotated)
            {
                center.X = _appScreen.ActualWidth - center.X;
                center.Y = _appScreen.ActualHeight - center.Y;
            }

            const int margin = 10;
            double maxWidth = Application.Current.MainWindow.Width;
            double maxHeight = Application.Current.MainWindow.Height;
            center.X = (Math.Max(0, center.X - obj.Width / 2) == 0) ? margin + obj.Width / 2 : center.X;
            center.X = (Math.Min(maxWidth, center.X + obj.Width / 2) == maxWidth)
                           ? maxWidth - obj.Width / 2 - margin
                           : center.X;

            center.Y = (Math.Max(0, center.Y - obj.Height / 2) == 0) ? margin + obj.Height / 2 : center.Y;
            center.Y = (Math.Min(maxHeight, center.Y + obj.Height / 2) == maxHeight)
                           ? maxHeight - obj.Height / 2 - margin
                           : center.Y;
            return center;
        }

        private bool IsObjAlreadyOpenAndNotAllowedToOpenAgain()
        {
            bool isObjAlreadyOpenAndNotAllowedToOpenAgain = false;
            //if single item -> move to front/old place and leave)
            if (_isSingleItem)
            {
                foreach (ScatterViewItem x in _scatterView.Items)
                {
                    var scatteredItem = ((Grid)(x).Content).Children[0];
                    if (scatteredItem.GetType() == _childControl.GetType())
                    {
                        isObjAlreadyOpenAndNotAllowedToOpenAgain = true;
                        x.Center = _pointOnScreen;
                        x.IsContainerActive = true;
                    }
                }
            }
            return isObjAlreadyOpenAndNotAllowedToOpenAgain;
        }

        private void SelectCurrentAddedItem()
        {
            foreach (ScatterViewItem x in _scatterView.Items)
            {
                x.IsContainerActive = false;
            }
            _item.IsContainerActive = true;
        }

        private void InitScatteredViewItem()
        {
            _grid = new Grid();
            _grid.Children.Add(_childControl);
            _button = new SurfaceButton
            {
                Content = new Border()
                {
                    BorderThickness = new Thickness(1.2),
                    BorderBrush = Brushes.Black,
                    CornerRadius = new CornerRadius(3),
                    Background = Brushes.White,
                    Child = new Label()
                    {
                        Background = (Brush)Application.Current.Resources["ItemClosedScatteredView"],
                        Width = 20,
                        Height = 20,
                        VerticalAlignment = VerticalAlignment.Center,
                        VerticalContentAlignment = VerticalAlignment.Center,
                        Padding = new Thickness(0),
                        Margin = new Thickness(0),
                    },
                    VerticalAlignment = VerticalAlignment.Stretch,
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    Margin = (_hasBorder) ? new Thickness(10, 8, 8, 6) : new Thickness(14, 4, 4, 10)
                },
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Top,
                HorizontalContentAlignment = HorizontalAlignment.Stretch,
                VerticalContentAlignment = VerticalAlignment.Stretch,
                Padding = new Thickness(0), Margin = new Thickness(0),
                Width = 60,
                Height = 30,
                MinHeight = 30,
                MinWidth = 60,
                Background = Brushes.Transparent
            };

            _grid.Children.Add(_button);
            _item = new WithoutTouchHandlerScatterItem {CanScale = _canScale, Background = Brushes.Transparent, Content = _grid};
            InitSize();
        }

        private void InitSize()
        {
            _item.Orientation = 0;
            _item.Center = _pointOnScreen;
            _item.Width = _childControl.Width;
            _item.Height = _childControl.Height;
            _item.MaxHeight = double.IsNaN(_childControl.MaxHeight) ? double.NaN : _childControl.MaxHeight;
            _item.MaxWidth = double.IsNaN(_childControl.MaxWidth) ? double.NaN : _childControl.MaxWidth;
            _item.MinWidth = double.IsNaN(_childControl.Width) ? 0 : _childControl.Width;
            _item.MinHeight = double.IsNaN(_childControl.Height) ? 0 : _childControl.Height;
        }

        private void ExpandSize(FrameworkElement frameworkitem)
        {
            frameworkitem.Width += BorderWidth * 2;
            frameworkitem.Height += BorderWidth * 2;

            frameworkitem.MinHeight += BorderWidth * 2;
            frameworkitem.MinWidth += BorderWidth * 2;

            frameworkitem.MaxHeight += BorderWidth * 2;
            frameworkitem.MaxWidth += BorderWidth * 2;
        }


        private void InitEvents()
        {

            _button.Click += (a, b) => RemoveScatteredItemFromView();

            //set min max after loading
            _item.Loaded += (a, b) =>
            {
                _item.MinHeight = _item.ActualHeight;
                _item.MinWidth = _item.ActualWidth;
            };

            _item.PreviewKeyDown += LeaveByEsc;
            _item.PreviewTouchDown += InputDown;
            _item.PreviewMouseDown += InputDown;
            _item.SizeChanged +=  ResizeObj;

        }

        private void InputDown(object sender, InputEventArgs args)
        {
            Border border = args.OriginalSource as Border;

            if (border != null 
                && border.BorderThickness.Top == BorderWidth 
                && ((int)((Border)args.OriginalSource).BorderThickness.Bottom == BorderWidth 
                && (args is TouchEventArgs)
                    ? _clickTouch.IsClickInTimeAndPlace(args.Device.GetPosition(Application.Current.MainWindow)) 
                    : _click.IsClickInTimeAndPlace(args.Device.GetPosition(Application.Current.MainWindow))))
            {
                var screenGrid = _appScreen.GetScreenGridForPoint(_item.ActualCenter);
                double width = screenGrid == null ? Application.Current.MainWindow.Width : screenGrid.Width;
                double height = screenGrid == null ? Application.Current.MainWindow.Height : screenGrid.Height;
                if (Math.Abs(_item.Width - width) > 0.01)
                {
                    Point startPoint = screenGrid == null ? new Point(width / 2, height / 2) : new Point(screenGrid.ScreenOffSet.X + width / 2, screenGrid.ScreenOffSet.Y + height / 2);
                    _oldCenter = _item.Center;
                    _oldHeight = _item.Height;
                    _oldOrientation = _item.Orientation;
                    _oldWidth = _item.Width;

                    _item.Width = width;
                    _item.Height = height;
                    _item.Center = startPoint;
                    _item.Orientation = 0;
                    args.Handled = true;
                }
                else if(_oldCenter.HasValue)
                {
                    _item.Center = _oldCenter.Value;
                    _item.Height = _oldHeight;
                    _item.Orientation = _oldOrientation;
                    _item.Width = _oldWidth;
                    args.Handled = true;
                }
                //RemoveScatteredItemFromView();
            }
        }

      
 


        private void LeaveByEsc(object sender, KeyEventArgs args)
        {
            if (args.Key == Key.Escape)
            {
                RemoveScatteredItemFromView();
            }
        }

        private void ResizeObj(object sender, SizeChangedEventArgs b)
        {
            if (!_canScale)
            {
                //set unscalable object to his preferred size 
                _childControl.Measure(new Size(double.MaxValue, double.MaxValue));
                _item.Width = _item.BorderThickness.Left  + _item.BorderThickness.Right + (double.IsNaN(_childControl.Width) ? _childControl.DesiredSize.Width : _childControl.Width);
                _item.Height = _item.BorderThickness.Top + _item.BorderThickness.Bottom + (double.IsNaN(_childControl.Height) ? _childControl.DesiredSize.Height : _childControl.Height);

                _childControl.Width = _item.Width - _item.BorderThickness.Left - _item.BorderThickness.Right;
                _childControl.Height = _item.Height - _item.BorderThickness.Top - _item.BorderThickness.Bottom;
            }
            else
            {
                _childControl.Width = Math.Max(0, b.NewSize.Width -
                                        (_item.BorderThickness.Left +
                                         _item.BorderThickness.Right +
                                         _item.Padding.Left + _item.Padding.Right));
                _childControl.Height = Math.Max(0, b.NewSize.Height -
                                         (_item.BorderThickness.Top +
                                          _item.BorderThickness.Bottom +
                                          _item.Padding.Top + _item.Padding.Bottom));
            }
        }

        private void InitBorder()
        {
            if (_hasBorder)
            {

                if ((null == _borderBinding))
                {
                    _item.BorderBrush = Application.Current.Resources["BorderBrushScatteredItems"] as Brush;
                }
                else
                {
                    _item.SetBinding(Control.BorderBrushProperty, _borderBinding);
                }

                _item.BorderThickness = new Thickness(BorderWidth);
                _button.Margin = new Thickness(0, -BorderWidth, -BorderWidth, 0);
                ExpandSize(_item);
                if (!string.IsNullOrEmpty(_title))
                {
                    Label textBox = new Label()
                                        {
                                            FontWeight = FontWeights.Bold,
                                            Content = _title,
                                            HorizontalAlignment = HorizontalAlignment.Center,
                                            VerticalAlignment = VerticalAlignment.Top,
                                            Margin = new Thickness(0, -BorderWidth, 0, 0),
                                            IsHitTestVisible =  false
                                        };
                    _grid.Children.Add(textBox);
                }
                if (!string.IsNullOrEmpty(_bottomText))
                {
                    Label textBox = new Label()
                                        {
                                            FontWeight = FontWeights.Bold,
                                            Content = _bottomText,
                                            HorizontalAlignment = HorizontalAlignment.Center,
                                            VerticalAlignment = VerticalAlignment.Bottom,
                                            Margin = new Thickness(0, 70, 0, -BorderWidth)
                                        };
                    _grid.Children.Add(textBox);
                }
            }
        }

        private void InitResizeUtil()
        {
            if (_appScreen.ConfigManager.Config.ShowResizeButton && _canScale)
            {
                _adornerLayer[0] = AdornerLayer.GetAdornerLayer(_item);
                _adorner[0] = new ResizingAdorner(_item);
                _adornerLayer[0].Add(_adorner[0]);
            }
        }
        #endregion
    }

}
